import { NamePath, StoreValue } from 'antd/lib/form/interface';
import { cloneDeep } from 'lodash';
import { HOOK_MARK } from './Context/FieldContext';
import type {
  Callbacks,
  InternalFormInstance,
  InternalHooks,
  InternalNamePath,
  Store,
} from './interface';
import type {
  FieldCombineEntity,
  FieldData,
  FieldEntity,
  InternalFieldData,
  NotifyInfo,
  ValuedNotifyInfo,
} from './interface/FieldEntityType';
import type {
  FieldError,
  ValidateMessages,
  ValidateOptions,
} from './interface/ValidateType';
import { allPromiseFinish } from './utils/asyncUtil';
import { get } from './utils/get';
import { defaultValidateMessages } from './utils/messages';
import { set } from './utils/set';
import { nameToNamePath } from './utils/valueUtil';

class FormStore {
  private formHooked: boolean = false;

  private store: Store = {};

  private fieldEntities: FieldCombineEntity[] = [];

  private initialValues: Store = {}; //Form.initiaValues = {users:[{name:'zyj'}]}   List.initialValue = [{name:'zyj'}] , 这里只存储了Form.initiaValues

  private callbacks: Callbacks = {};

  private validateMessages: ValidateMessages = {};

  // ======================== FormInstance ========================
  public getForm: () => InternalFormInstance = () => ({
    getFieldValue: this.getFieldValue,
    getFieldsValue: this.getFieldsValue,
    setFieldValue: this.setFieldValue,
    setFieldsValue: this.setFieldsValue,
    setFields: this.setFields,
    resetFields: this.resetFields,
    validateFields: this.validateFields,
    submit: this.submit,
    getInternalHooks: this.getInternalHooks,
  });

  // ============================ Submit ============================
  private submit: InternalFormInstance['submit'] = () => {
    const { onFinish, onFinishFailed } = this.callbacks;
    this.validateFields()
      .then((values) => {
        onFinish?.(values);
      })
      .catch((errorInfo) => {
        onFinishFailed?.(errorInfo);
      });
  };

  // ======================== Internal Hooks ========================
  private getInternalHooks: InternalFormInstance['getInternalHooks'] = (
    secret,
  ) => {
    if (secret === HOOK_MARK) {
      return {
        updateValue: this.updateValue,
        initEntityValue: this.initEntityValue,
        registerField: this.registerField,
        cancelField: this.cancelField,
        setInitialValues: this.setInitialValues,
        destroyForm: this.destroyForm,
        setCallbacks: this.setCallbacks,
        getFields: this.getFields,
        setValidateMessages: this.setValidateMessages,
        getInitialValue: this.getInitialValue,
      };
    }
    return null;
  };

  //未完成
  private destroyForm: InternalHooks['destroyForm'] = () => {
    console.log('--Form组件卸载--');
  };

  private setCallbacks: InternalHooks['setCallbacks'] = (callbacks) => {
    this.callbacks = callbacks;
  };
  private setValidateMessages: InternalHooks['setValidateMessages'] = (
    validateMessages,
  ) => {
    this.validateMessages = validateMessages;
  };

  // ============================ Store =============================
  /**
   *
   *
   * @private
   * @memberof FormStore
   * @description 凡是执行updateStore(setInitialValues+initEntityValue 中的updateStore 最后在registerField中 执行到onStoreChange ) 的地方就得执行onStoreChange
   */
  private updateStore = (nextStore: Store) => {
    this.store = cloneDeep(nextStore);
  };

  // ============================ Fields ============================

  /**
   * Get registered field entities.
   * @param pure Only return field which has a `name`. Default: false
   * @description <Field<FormData> dependencies={['name']} />
   */
  private _getFieldEntities = (pure: boolean = false) => {
    if (!pure) {
      return this.fieldEntities;
    }

    return this.fieldEntities.filter((field) => field.getNamePath().length); // 过滤出name存在的Filed 实例 ,Form.List 对应的 Field.getNamePath() === ListContext.prefixName
  };

  /**
   *
   * @param nameList
   * @returns
   * @description 根据nameList [ ['user','name'], ['user','age'] ]  / [['users',0,'name'],['users',0,'age']] 返回 Fields
   */
  private _getFieldEntitiesForNamePathList: (
    nameList?: NamePath[],
  ) => FieldCombineEntity[] = (nameList) => {
    const allHasNameFieldEnties = this._getFieldEntities(true);
    if (!nameList || !nameList.length) {
      return allHasNameFieldEnties;
    }

    return allHasNameFieldEnties.reduce(
      (
        prev: FieldCombineEntity[],
        curr: FieldCombineEntity,
        index: number,
        array: FieldCombineEntity[],
      ) => {
        const existFieldEntityIndex = nameList.findIndex(
          (item) =>
            nameToNamePath(item).join(',') === curr.getNamePath().join(','),
        );
        if (existFieldEntityIndex > -1) {
          prev.push(curr);
        }
        return prev;
      },
      [],
    );
  };

  /**
   *
   *
   * @private
   * @type {InternalHooks['registerField']}
   * @memberof FormStore
   * @description 执行时机 Field--ComponentDidMount--registerField(Field)
   */
  private registerField: InternalHooks['registerField'] = (fieldEntity) => {
    this.fieldEntities.push(fieldEntity);
    const prevStore = cloneDeep(this.store);
    // <Field  shouldUpdate = {(prevValues,currValues) => currValues.userName !== prevValues.userName} >  {(control,meta,form) => <Field name="password"> } </Field>
    // <Field name="userName" initialValue="zyj" shouldUpdate = {(prevValues,currValues) => currValues.name !== prevValues.name} >{...}</Field>
    // 为了兼容初始化时， <Field name="password"> 也能渲染
    if (fieldEntity.props.initialValue !== undefined) {
      this.notifyObservers(prevStore, [], {
        type: 'valueUpdate',
        source: 'internal',
      });
    }

    // this.notifyWatch([namePath]); ...未完成
  };

  /**
   *
   * @param fieldEntity
   * @description 使用场景 src/pages/FormPageDependencies/index.tsx  <Field dependencies={['name']} > { (control,meta,form) => { if (...) return <Field name="password">  else return null  } } </Field>
   * @description Filed的卸载 一定是由于 Field.onChange --updateValue 引起的，而updateValue 会触发  'valueUpdate','dependenciesUpdate' ,'onValuesChange', 'onFieldsChange'
   */
  private cancelField: InternalHooks['cancelField'] = (fieldEntity) => {
    this.fieldEntities = this.fieldEntities.filter(
      (item) => item !== fieldEntity,
    );
    const prevStore = cloneDeep(this.store);
    // // --value
    const namePath = fieldEntity.getNamePath();
    if (namePath.length && !fieldEntity.props.isListField) {
      // List时 ，List 的remove对于List来说是onChange对于List的子级Field来说是cancelField, List.remove ==> [{name:'zyj1',age:30},{name:'zyj2',age:31}] --> [{name:'zyj1',age:30}]
      this.updateStore(set(this.store, namePath, undefined)); // 这里 set({'users':[{name:'zyj1',age:30}], ['users',1,'name']} , undefiend) ==> {'users':[{name:'zyj1',age:30},{name:undefined,age:undefined}]
      this.notifyObservers(prevStore, [namePath], { type: 'remove' });
      // dependencies= [name]
      this._triggerDependenciesUpdate(prevStore, namePath);
    }

    // this.notifyWatch([namePath]); //未完成
  };

  /**
   *
   *
   * @private
   * @type {InternalFormInstance['setFields']}
   * @memberof FormStore
   * @description 1. <Form fields={[ {name:['user','name'],value:'zyj1'} ]}> /  <Form fields={[ {name:['users',0,'name'],value:'zyj1'} ]}> 初始化 formInstance.setFields() 2.setFields([ {name:['user','name'],value:'zyj' } ])
   */
  private setFields: InternalFormInstance['setFields'] = (fields) => {
    if (!fields.length) return;
    const prevStore = cloneDeep(this.store);
    const namePathList: InternalNamePath[] = [];
    const fieldDataMap = new Map<string, FieldData>();
    fields.forEach((fieldData: FieldData) => {
      const { name, ...data } = fieldData;
      const namePath = nameToNamePath(name);
      namePathList.push(namePath);
      if ('value' in data) {
        this.updateStore(set(this.store, namePath, data.value)); //更新store
      }
      fieldDataMap.set(namePath.join(','), fieldData);
    });

    this.notifyObservers(prevStore, namePathList, {
      type: 'setField',
      data: fieldDataMap,
    });

    // this.notifyWatch(namePathList); 未完成
  };

  /**
   *
   * @param namePathList
   * @description 执行时机 resetFields, 把store[name] 重置为 Form.initialvalues[name] 或者 Field.initialValue
   */
  private _resetWithFieldInitialValue = (namePathList: InternalNamePath[]) => {
    const tempFieldEntities =
      this._getFieldEntitiesForNamePathList(namePathList);
    tempFieldEntities.forEach((field) => {
      const namePath = field.getNamePath();
      const fieldInitialValue = field.props.initialValue ?? undefined;
      const fieldInitialValueFromForm = this.getInitialValue(namePath);
      const currValue = fieldInitialValueFromForm ?? fieldInitialValue;
      if (field.props.isListField) {
        // List.value是包含List下的Field.value的， set Field.value会影响List.value 详见cancelField
        const existNamePathIndex = namePathList.findIndex(
          (namePathItem) => namePathItem.join(',') === namePath.join(','),
        );
        if (existNamePathIndex > -1) {
          this.updateStore(set(this.store, namePath, currValue));
        }
      } else {
        this.updateStore(set(this.store, namePath, currValue));
      }
    });
  };
  /**
   *
   *
   * @private
   * @type {InternalFormInstance['resetFields']}
   * @memberof FormStore
   * @param fields [['user','name'],['users',0,'name']]
   * @description resetFields -- onStoreChange -- reFresh -- 会导致Field 先ComponentWillNumount(cancelField)再ComponentDidMount(registerField)
   */
  private resetFields: InternalFormInstance['resetFields'] = (fields) => {
    //value, Filed
    const prevStore = cloneDeep(this.store);
    const namePathList: InternalNamePath[] = [];
    if (Array.isArray(fields)) {
      fields.forEach((name) => {
        const namePath = nameToNamePath(name);
        namePathList.push(namePath);
      });
    } else {
      this.updateStore(this.initialValues);
    }
    this._resetWithFieldInitialValue(namePathList);
    this.notifyObservers(prevStore, namePathList, { type: 'reset' });

    // this.notifyWatch(namePathList); 未完成
  };

  private getFields: InternalHooks['getFields'] = () => {
    const tempFieldEntities = this._getFieldEntities(true);
    return tempFieldEntities.map((field) => {
      const meta = field.getMeta();
      const namePath = field.getNamePath();
      const value = this.getFieldValue(namePath);
      return {
        ...meta,
        value,
      };
    });
  };

  // =========================== Field Values ===========================

  private _getFieldValuesNamePathList = (
    store: Store,
    namePathList: InternalNamePath[],
  ): Store => {
    let newStore = {};
    namePathList.forEach((namePath) => {
      const value = get(store, namePath);
      newStore = set(newStore, namePath, value);
    });

    return newStore;
  };

  /**
   *
   * @param field
   * @description 执行时机 Field.constructor中执行 List.initialValue = [{name:'zyj'}]
   * @description 如果Filed.getNamePath() === [],说明该Field 没有设置name,那么this.getFieldValue([]) === undefined
   */
  private initEntityValue: InternalHooks['initEntityValue'] = (field) => {
    const { initialValue } = field.props;
    const namePath = field.getNamePath();
    const formInitialValue = this.getFieldValue(namePath);
    if (initialValue && formInitialValue === undefined) {
      this.updateStore(set(this.store, namePath, initialValue)); //更新store
    }
  };

  /**
   *
   *
   * @private
   * @type {InternalHooks['setInitialValues']}
   * @memberof FormStore
   * @param init true:初始化
   * @description 执行时机 Form 初始化时
   */
  private setInitialValues: InternalHooks['setInitialValues'] = (
    values,
    init,
  ) => {
    this.initialValues = values || {};
    if (init) {
      const nextStore = { ...this.store, ...values };
      this.updateStore(nextStore);
    }
  };

  /**
   *
   *
   * @private
   * @type {InternalHooks['getInitialValue']}
   * @memberof FormStore
   * @description this.initialValues 是不包含 Filed.initialValue 的
   */
  private getInitialValue: InternalHooks['getInitialValue'] = (namePath) => {
    if (!namePath || !namePath.length) return this.initialValues;
    return get(this.initialValues, namePath as InternalNamePath);
  };

  //FormInstance
  private getFieldValue: InternalFormInstance['getFieldValue'] = (name) => {
    const namePath: InternalNamePath = nameToNamePath(name);
    return get(this.store, namePath);
  };

  /**
   *1.nameList === [ ['user','name'], ['user','age'] ]  / [['users',0,'name'],['users',0,'age']] 时 按照Field.name 一一映射返回
   *2.nameList === 'all' return this.store, 不循环fieldEntities,可以在Field尚未渲染完成时，获取数据
   *3.nameList === 'default' 循环所有Field, 除了List 下的Field.name，其余按照Field.name 一一映射返回
   *4.nameList === 'asFormListItem' 循环所有Field,按照 Field.name 一一映射返回
   * 注：2是直接返回store ，1,3,4是根据Field.name 从store找出数据(_getFieldValuesNamePathList),找不到时会Object[name] = undefined;最终 return Object
   * @private
   * @type {InternalFormInstance['getFieldsValue']}
   * @memberof FormStore
   * @param {namePathList} 获取范围
   * @description 执行时机：1.Field-render-getControled, 2.Form-setFields, 3.用户触发getFieldsValue api
   */
  private getFieldsValue: InternalFormInstance['getFieldsValue'] = (
    nameList = 'default',
  ) => {
    if (nameList === 'all') {
      return cloneDeep(this.store);
    }
    const fieldEntities = this._getFieldEntitiesForNamePathList(
      Array.isArray(nameList) ? nameList : undefined,
    );
    const filteredNameList: InternalNamePath[] = [];
    fieldEntities.forEach((fieldEntity: FieldEntity) => {
      if (nameList === 'default' && fieldEntity.isFieldListField()) {
        // 当前Field实例是 Form.List 的子级Field
        return;
      }
      if (nameList === 'asFormListItem' && fieldEntity.isFieldList()) {
        // 当前Field实例是 Form.List
        return;
      }
      filteredNameList.push(fieldEntity.getNamePath());
    });
    return this._getFieldValuesNamePathList(this.store, filteredNameList);
  };

  /**
   *
   *
   * @private
   * @type {InternalFormInstance['setFieldValue']}
   * @memberof FormStore
   * @description setFieldValue(['user','name'],'zyj')-- setFields([ {name:['user','name'],value:'zyj' } ])
   */
  private setFieldValue: InternalFormInstance['setFieldValue'] = (
    name,
    value,
  ) => {
    this.setFields([{ name, value }]);
  };

  /**
   *
   * @param values
   * @description 每个Field都要reRender() setFieldsValue({ user:{name:'zyj'}, users:[{name:'zyj'}] })
   */
  private setFieldsValue: InternalFormInstance['setFieldsValue'] = (values) => {
    const prevStore = cloneDeep(this.store);
    this.store = { ...this.store, ...values };
    this.updateStore(this.store);
    this.notifyObservers(prevStore, [], {
      type: 'valueUpdate',
      source: 'external',
    });
  };

  // =========================== Observer ===========================

  /**
   *
   *
   * @private
   * @param {Store} prevStore
   * @param {InternalNamePath[]} namePathList 本次操作影响到的Field范围
   * @param {NotifyInfo} info
   * @memberof FormStore
   * @description 执行时机 updateValue
   * @description 因为任意Field执行操作 'updateVale' , 'setField' , 'reset', 'remove' 都会触发设置了shoudUpdate的Field的 shoudUpdate函数来决定是否reRender,所以要循环所有fieldEntities
   */
  private notifyObservers(
    prevStore: Store,
    namePathList: InternalNamePath[],
    info: NotifyInfo,
  ) {
    const mergedInfo: ValuedNotifyInfo = {
      ...info,
      store: this.getFieldsValue('all'),
    };
    const tempFieldEntities = this._getFieldEntities();
    tempFieldEntities.forEach(({ onStoreChange }) => {
      onStoreChange(prevStore, namePathList, mergedInfo);
    });
  }

  /**
   *
   * @private
   * @memberof FormStore
   * @description <Field name="password" /> 、 <Field name="password2" dependencies={["password"]} /> 、<Field dependencies={["password2"]} />
   * @description Field.password onChange时 如果Field.password2没有执行过onChage/initialValue===undefined,此时Field.password2 不会执行validateFields
   */
  private _getDependencRecursionDirtyFieldNamePathList = (
    rootNamePath: InternalNamePath,
  ): InternalNamePath[] => {
    const children: Set<FieldEntity> = new Set();
    const childrenFields: InternalNamePath[] = [];

    // 把Field按照 dependency分类： {'password':[Field.password2],'password2':[Field.undefined]}
    const dependencies2fields = new Map<string, Set<FieldCombineEntity>>();

    this._getFieldEntities().forEach((field) => {
      const { dependencies } = field.props;
      (dependencies || []).forEach((dependency) => {
        const dependencyNamePathStr = nameToNamePath(dependency).join(',');
        const tempSet =
          dependencies2fields.get(dependencyNamePathStr) ||
          new Set<FieldCombineEntity>();
        tempSet.add(field);
        dependencies2fields.set(dependencyNamePathStr, tempSet);
      });
    });

    const fillChildren = (namePath: InternalNamePath) => {
      const fields = dependencies2fields.get(namePath.join(',')) || new Set();
      fields.forEach((field) => {
        if (!children.has(field)) {
          children.add(field);

          const fieldNamePath = field.getNamePath();
          if (field.isFieldDirty() && fieldNamePath.length) {
            childrenFields.push(fieldNamePath);
            fillChildren(fieldNamePath);
          }
        }
      });
    };

    fillChildren(rootNamePath);

    return childrenFields;
  };

  /**
   *
   *
   * @private
   * @memberof FormStore
   * @description 触发时机：updateValue / cancelField  <Field<FormData> dependencies={['name']}>
   */
  private _triggerDependenciesUpdate: (
    prevStore: Store,
    rootNamePath: InternalNamePath,
  ) => InternalNamePath[] = (prevStore, rootNamePath) => {
    const depReCursionDirtyFieldNamePathList =
      this._getDependencRecursionDirtyFieldNamePathList(rootNamePath);

    if (depReCursionDirtyFieldNamePathList.length) {
      this.validateFields(depReCursionDirtyFieldNamePathList).catch((err) => {
        console.log(
          '----_triggerDependenciesUpdate--validateFields--catch--err--:',
          err,
        );
      });
    }
    //Field[undefined]也要reRender
    this.notifyObservers(
      prevStore,
      [rootNamePath, ...depReCursionDirtyFieldNamePathList],
      { type: 'dependenciesUpdate' },
    );
    return depReCursionDirtyFieldNamePathList;
  };

  private _triggerOnFieldsChange = (namePathList: InternalNamePath[]) => {
    const fieldDataList = this.getFields();
    const { onFieldsChange } = this.callbacks;
    if (onFieldsChange) {
      const changedFields = fieldDataList.reduce(
        (
          prev: InternalFieldData[],
          curr: InternalFieldData,
          index: number,
          array: InternalFieldData[],
        ) => {
          const { name } = curr;
          const existNamePathIndex = namePathList.findIndex(
            (namePathItem) =>
              namePathItem.join(',') === nameToNamePath(name).join(','),
          );
          if (existNamePathIndex > -1) {
            prev.push(curr);
          }
          return prev;
        },
        [],
      );
      onFieldsChange(changedFields, fieldDataList);
    }
  };

  /**
   *
   *
   * @private
   * @param {InternalNamePath} namePath
   * @param {StoreValue} value
   * @memberof FormStore
   * @description  执行时机： 1.Field-children-onChange  Field.onChange(newValue) 并没有执行this.setState() ,classComponent并不会自动执行 render、shouldComponentUpdate, 所以要通过forceUpdate()达到目的
   * @description 会触发有shouldUpdate的Field的shouldUpdate--reRender
   */
  private updateValue: InternalHooks['updateValue'] = (namePath, value) => {
    const prevStore = cloneDeep(this.store);
    if (value === get(prevStore, namePath)) {
      return;
    }
    this.updateStore(set(this.store, namePath, value)); //更新store

    this.notifyObservers(prevStore, [namePath], {
      type: 'valueUpdate',
      source: 'internal',
    }); //reRender()

    // this.notifyWatch([namePath]); //未完成
    // dependencies= [name]
    const depReCursionDirtyFieldNamePathList: InternalNamePath[] =
      this._triggerDependenciesUpdate(prevStore, namePath);
    // trigger callback function
    const { onValuesChange } = this.callbacks;
    if (onValuesChange) {
      const newStore = this.getFieldsValue('all');
      const changedValues = this._getFieldValuesNamePathList(newStore, [
        namePath,
      ]);
      onValuesChange(changedValues, newStore);
    }
    this._triggerOnFieldsChange([
      namePath,
      ...depReCursionDirtyFieldNamePathList,
    ]);
  };

  /**
   *
   *
   * @private
   * @type {InternalFormInstance['validateFields']}
   * @memberof FormStore
   * @param arg1arg2 updateValue(onChage):  [namePath], {triggerName?:string} / submit:  arg1 :undefined , arg2?:  {validateOnly?:boolean,recursive?:boolean}
   */
  private validateFields: InternalFormInstance['validateFields'] = (
    arg1,
    arg2,
  ) => {
    let nameList: NamePath[] = [];
    let options: ValidateOptions = {};

    nameList = arg1 ?? [];
    options = arg2 ?? {};
    const needValidateNamePathList: InternalNamePath[] = [];
    const promiseList: Promise<FieldError>[] = []; //FieldError : {name:InternalPath,errors:string[],warnings:string[]}
    //1.获取需要验证的FieldEntities
    const filedEntities = this._getFieldEntities(true);
    // console.log('------nameList--------:',nameList[0]);
    filedEntities.forEach((filed) => {
      if (!filed.props.rules || !filed.props.rules.length) {
        return;
      }
      const namePath = filed.getNamePath();
      const namePathExistIndex = nameList
        .map(nameToNamePath)
        .findIndex(
          (namePathItem) => namePathItem.join(',') === namePath.join(','),
        );
      const needValidate = !nameList.length
        ? true
        : namePathExistIndex > -1
        ? true
        : false;
      if (needValidate === true) {
        needValidateNamePathList.push(namePath);
        //2.调用 field.validateRules
        const validatePromise = filed.validateRules({
          validateMessages: {
            ...defaultValidateMessages,
            ...this.validateMessages,
          },
          ...options,
        });
        promiseList.push(validatePromise);
      }
    });
    // 3. 把所有Field的检测结果(Promise<FieldError>)合到一个promise中 ,只要有一个Field没有检测通过，summaryPromise就只存在reject
    const summaryPromise: Promise<FieldError[]> = allPromiseFinish(promiseList);

    // 5. FormInstance.validateFields() --- 返回Promise
    const returnPromise: Promise<Store> = new Promise((resolve, reject) => {
      summaryPromise
        .catch((fieldErrors) => {
          return { isReject: true, fieldErrors: fieldErrors };
        })
        .then((fieldErrors) => {
          // console.log('--5. 所有Field验证完毕--触发onFieldsChange--');
          let finalFieldErrors: FieldError[] = [];
          if (Array.isArray(fieldErrors)) {
            finalFieldErrors = fieldErrors;
          } else if (fieldErrors.isReject) {
            finalFieldErrors = fieldErrors.fieldErrors;
          }

          const resultNamePathList: InternalNamePath[] = finalFieldErrors.map(
            (errorItem: FieldError) => errorItem.name,
          );

          const values = this.getFieldsValue(
            nameList.length > 0
              ? resultNamePathList
              : filedEntities.map((field) => field.getNamePath()),
          );
          //注：console.log 1,3 已经reRender,且this.store已经是最新值了 这里没必要
          // this.notifyObservers(this.store, resultNamePathList, {
          //   type: 'validateFinish',
          // });
          this._triggerOnFieldsChange(resultNamePathList);
          if (Array.isArray(fieldErrors)) {
            resolve(values);
          } else if (fieldErrors.isReject) {
            reject({ values, errorFields: fieldErrors.fieldErrors });
          }
        });
    });

    // console.log('--2.所有Filed验证开始--触发onFieldschange--');
    this._triggerOnFieldsChange(needValidateNamePathList);
    return returnPromise;
  };
}

export default FormStore;
